"""
自动化执行模块 - 完整修复版本
负责执行记录的自动化操作
"""

import time
import threading
import logging
from typing import List, Tuple, Dict, Any, Callable, Optional
import pyautogui
import pyperclip

from .point_manager import PointManager
from .image_processor import ImageProcessor

logger = logging.getLogger(__name__)


class AutomationExecutor:
    """自动化执行器 - 完整修复版本"""

    def __init__(self, point_manager: PointManager, image_processor: ImageProcessor = None):
        self.point_manager = point_manager
        self.image_processor = image_processor or ImageProcessor()
        self.is_running = False
        self.is_paused = False
        self.current_index = 0
        self._stop_event = threading.Event()
        self._pause_event = threading.Event()
        self._pause_event.set()  # 初始状态为不暂停
        self._execution_lock = threading.Lock()  # 防止并发执行

    def execute_all_actions(self, config: Dict[str, Any], status_callback: Optional[Callable] = None) -> bool:
        """执行所有记录的操作 - 线程安全版本"""
        # 防止并发执行
        if not self._execution_lock.acquire(blocking=False):
            self._update_status(status_callback, "已有执行在进行中")
            return False

        try:
            self.is_running = True
            self.is_paused = False
            self.current_index = 0
            self._stop_event.clear()
            self._pause_event.set()

            # 验证配置参数
            validated_config = self._validate_and_get_config(config)
            if not validated_config:
                self._update_status(status_callback, "配置参数无效")
                return False

            global_delay = validated_config['global_delay']
            global_interval = validated_config['global_interval']
            input_method = validated_config['input_method']
            clear_before_input = validated_config['clear_before_input']
            enter_after_input = validated_config['enter_after_input']

            # 初始延迟
            if not self._wait_with_stop(global_delay, status_callback, "准备执行"):
                return False

            points = self.point_manager.get_points_as_objects()
            total_points = len(points)

            if total_points == 0:
                self._update_status(status_callback, "没有可执行的点")
                return False

            # 执行前安全检查
            if not self._pre_execution_check():
                self._update_status(status_callback, "执行前安全检查失败")
                return False

            # 执行所有点
            success_count = 0
            for i, point in enumerate(points):
                if self._stop_event.is_set():
                    self._update_status(status_callback, "执行已停止")
                    return False

                # 检查暂停状态
                if not self._check_pause_state():
                    return False

                self.current_index = i
                self._update_status(status_callback, f"执行操作 {i+1}/{total_points}")

                # 执行单个点
                success = self._execute_single_point(
                    point, input_method, clear_before_input,
                    enter_after_input, status_callback
                )

                if success:
                    success_count += 1
                else:
                    self._update_status(status_callback, f"执行第 {i+1} 个点失败")
                    if validated_config.get('stop_on_error', True):
                        return False
                    # 继续执行下一个点

                # 点之间的间隔（最后一个点后不需要等待）
                if i < total_points - 1:
                    if not self._wait_with_stop(global_interval, status_callback, "等待间隔"):
                        return False

            self._update_status(status_callback, f"完成 {success_count}/{total_points} 个操作")
            return success_count > 0

        except Exception as e:
            logger.error(f"执行过程中出错: {e}")
            self._update_status(status_callback, f"执行出错: {e}")
            return False
        finally:
            self._cleanup_execution()
            self._execution_lock.release()

    def _validate_and_get_config(self, config: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """验证并获取配置参数"""
        try:
            validated = {}

            # 验证数值参数
            numeric_fields = {
                'global_delay': (0, 300, 3),
                'global_interval': (0, 60, 0.5),
                'delay_after': (0, 60, 0.5)
            }

            for field, (min_val, max_val, default) in numeric_fields.items():
                try:
                    value = float(config.get(field, default))
                    if min_val <= value <= max_val:
                        validated[field] = value
                    else:
                        logger.warning(f"{field} 值 {value} 超出范围，使用默认值 {default}")
                        validated[field] = default
                except (ValueError, TypeError):
                    validated[field] = default

            # 验证字符串参数
            validated['input_method'] = config.get('input_method', 'paste')
            if validated['input_method'] not in ['paste', 'keyboard', 'image']:
                validated['input_method'] = 'paste'

            # 验证布尔参数
            validated['clear_before_input'] = bool(config.get('clear_before_input', True))
            validated['enter_after_input'] = bool(config.get('enter_after_input', False))
            validated['stop_on_error'] = bool(config.get('stop_on_error', True))

            return validated

        except Exception as e:
            logger.error(f"配置验证失败: {e}")
            return None

    def _pre_execution_check(self) -> bool:
        """执行前安全检查"""
        try:
            # 检查pyautogui是否可用
            try:
                screen_size = pyautogui.size()
                logger.info(f"屏幕尺寸: {screen_size}")
            except Exception as e:
                logger.error(f"无法获取屏幕尺寸: {e}")
                return False

            # 检查pyperclip是否可用
            try:
                test_text = "自动化测试"
                pyperclip.copy(test_text)
                copied = pyperclip.paste()
                if copied != test_text:
                    logger.warning("剪贴板功能可能不正常")
            except Exception as e:
                logger.warning(f"剪贴板检查失败: {e}")

            # 检查点数据
            points = self.point_manager.get_points_as_objects()
            for i, point in enumerate(points):
                if not self._validate_coordinates(point.x, point.y):
                    logger.error(f"点 {i+1} 坐标无效: ({point.x}, {point.y})")
                    return False

            return True

        except Exception as e:
            logger.error(f"执行前检查失败: {e}")
            return False

    def _execute_single_point(self, point, input_method: str, clear_before_input: bool,
                            enter_after_input: bool, status_callback: Optional[Callable]) -> bool:
        """执行单个点的操作"""
        try:
            # 执行点击操作
            click_success = self._perform_click(point.x, point.y, point.click_type)
            if not click_success:
                return False

            # 点击后延迟
            if not self._wait_with_stop(point.delay_after, status_callback, "点击后延迟"):
                return False

            # 如果有内容，执行输入操作
            if point.text:
                if point.is_image:
                    # 图片输入
                    return self._input_image(point.text, status_callback)
                else:
                    # 文本输入
                    return self._input_text(
                        point.text, input_method, clear_before_input,
                        enter_after_input, status_callback
                    )

            return True

        except Exception as e:
            logger.error(f"执行点操作失败: {e}")
            return False

    def _perform_click(self, x: int, y: int, click_type: str) -> bool:
        """执行点击操作"""
        try:
            # 验证坐标有效性
            if not self._validate_coordinates(x, y):
                logger.error(f"坐标无效: ({x}, {y})")
                return False

            # 移动鼠标到目标位置
            pyautogui.moveTo(x, y, duration=0.1)
            time.sleep(0.05)  # 确保移动完成

            if click_type == "单击":
                pyautogui.click()
            elif click_type == "双击":
                pyautogui.doubleClick()
            elif click_type == "右键点击":
                pyautogui.rightClick()
            else:
                logger.warning(f"未知的点击类型: {click_type}，使用默认单击")
                pyautogui.click()

            logger.info(f"已{click_type}点 ({x}, {y})")
            return True

        except Exception as e:
            logger.error(f"点击操作失败: {e}")
            return False

    def _validate_coordinates(self, x: int, y: int) -> bool:
        """验证坐标有效性"""
        try:
            screen_width, screen_height = pyautogui.size()
            return 0 <= x < screen_width and 0 <= y < screen_height
        except Exception:
            # 如果获取屏幕尺寸失败，使用保守验证
            return 0 <= x < 10000 and 0 <= y < 10000

    def _input_text(self, text: str, input_method: str, clear_before_input: bool,
                   enter_after_input: bool, status_callback: Optional[Callable]) -> bool:
        """输入文本"""
        try:
            if input_method == "paste":
                # 保存当前剪贴板内容
                original_clipboard = ""
                try:
                    original_clipboard = pyperclip.paste()
                except:
                    pass

                # 使用粘贴方式
                pyperclip.copy(text)
                self._adaptive_sleep(0.2)  # 给剪贴板操作时间
                pyautogui.hotkey('ctrl', 'v')

                # 恢复原始剪贴板内容
                self._adaptive_sleep(0.1)
                try:
                    pyperclip.copy(original_clipboard)
                except:
                    pass

                logger.info(f"已使用粘贴方式输入: '{text}'")
            else:
                # 键盘输入方式
                if clear_before_input:
                    pyautogui.hotkey('ctrl', 'a')
                    self._adaptive_sleep(0.3)
                    pyautogui.press('backspace')
                    self._adaptive_sleep(0.2)

                # 分段输入，避免输入过快
                chunk_size = 50
                for i in range(0, len(text), chunk_size):
                    if self._stop_event.is_set():
                        return False
                    chunk = text[i:i + chunk_size]
                    pyautogui.write(chunk, interval=0.05)
                    self._adaptive_sleep(0.1)

                logger.info(f"已使用键盘输入方式输入: '{text}'")

            # 按回车确认
            if enter_after_input:
                self._adaptive_sleep(0.3)
                pyautogui.press('enter')
                logger.info("已按回车确认")

            return True

        except Exception as e:
            logger.error(f"文本输入失败: {e}")
            return False

    def _adaptive_sleep(self, delay: float):
        """自适应睡眠，考虑停止事件"""
        start_time = time.time()
        while time.time() - start_time < delay:
            if self._stop_event.is_set():
                break
            time.sleep(0.05)

    def _input_image(self, image_path: str, status_callback: Optional[Callable]) -> bool:
        """输入图片"""
        try:
            if not self.image_processor.copy_image_to_clipboard(image_path):
                logger.error(f"图片复制到剪贴板失败: {image_path}")
                return False

            self._adaptive_sleep(0.3)  # 等待剪贴板操作完成
            pyautogui.hotkey('ctrl', 'v')
            self._adaptive_sleep(0.5)  # 等待粘贴完成

            logger.info(f"已输入图片: {image_path}")
            return True

        except Exception as e:
            logger.error(f"图片输入失败: {e}")
            return False

    def _check_pause_state(self) -> bool:
        """检查暂停状态"""
        while self.is_paused and not self._stop_event.is_set():
            time.sleep(0.1)
        return not self._stop_event.is_set()

    def _wait_with_stop(self, delay: float, status_callback: Optional[Callable], message: str = "") -> bool:
        """带停止检查的等待"""
        if delay <= 0:
            return True

        start_time = time.time()
        remaining = delay

        while remaining > 0:
            if self._stop_event.is_set():
                return False

            # 更新状态
            if status_callback and message:
                self._update_status(status_callback, f"{message}: {remaining:.1f}秒")

            # 分段等待，便于及时响应停止信号
            wait_time = min(0.5, remaining)
            time.sleep(wait_time)
            remaining = delay - (time.time() - start_time)

        return True

    def _update_status(self, status_callback: Optional[Callable], message: str):
        """更新状态"""
        if status_callback:
            try:
                status_callback(message)
            except Exception as e:
                logger.error(f"状态回调失败: {e}")

        logger.debug(f"状态更新: {message}")

    def _cleanup_execution(self):
        """清理执行状态"""
        self.is_running = False
        self.is_paused = False
        self._stop_event.clear()
        self._pause_event.set()
        logger.info("执行清理完成")

    def stop_execution(self) -> None:
        """停止执行"""
        self._stop_event.set()
        self.is_running = False
        logger.info("执行已停止")

    def pause_execution(self) -> None:
        """暂停执行"""
        self.is_paused = True
        self._pause_event.clear()
        logger.info("执行已暂停")

    def resume_execution(self) -> None:
        """恢复执行"""
        self.is_paused = False
        self._pause_event.set()
        logger.info("执行已恢复")

    def is_executing(self) -> bool:
        """是否正在执行"""
        return self.is_running

    def get_current_progress(self) -> Tuple[int, int]:
        """获取当前执行进度 (当前索引, 总数量)"""
        total = self.point_manager.get_point_count()
        return self.current_index, total

    def is_pyautogui_available(self) -> bool:
        """检查pyautogui是否可用"""
        try:
            pyautogui.size()
            return True
        except Exception:
            return False